#include "StdAfx.h"

#include "ContractMatcher.h"
#include "Utils/Logger.h"

using namespace std;


ContractMatcher::ContractMatcher(int contract,int minPriceUnit)
:m_contract(contract),m_minPriceUnit(minPriceUnit)
{
}

ContractMatcher& ContractMatcher::Update(PendingMarket const& pendingMarket)
{
	m_markets.Update(pendingMarket);
	return *this;
}

ContractMatcher& ContractMatcher::Buy(int price, int quantity)
{
	m_markets.Buy(price,quantity);
	return *this;
}

ContractMatcher& ContractMatcher::Sell(int price, int quantity)
{
	m_markets.Sell(price,quantity);
	return *this;
}

ContractMatcher& ContractMatcher::AddBuy(int price, int quantity)
{
	m_markets.AddBuy(price,quantity);
	return *this;
}

ContractMatcher& ContractMatcher::AddSell(int price, int quantity)
{
	m_markets.AddSell(price,quantity);
	return *this;
}

void ContractMatcher::LogPendingOrders()
{
	char buff[1024] = "";
	PendingMarketMap::const_reverse_iterator buyIter = GetBuys().rbegin();
	PendingMarketMap::const_iterator sellIter = GetSells().begin();
	while (buyIter != GetBuys().rend() || sellIter != GetSells().end())
	{
		int buyPrice = 0;
		int buyQuantity = 0;
		int sellPrice = 0;
		int sellQuantity = 0;

		while (buyIter != GetBuys().rend() && buyQuantity == 0)
		{
			buyPrice = buyIter->first;
			buyQuantity = buyIter->second;
			++buyIter;
		}
		if (sellIter != GetSells().end() && sellQuantity == 0)
		{
			sellPrice = sellIter->first;
			sellQuantity = sellIter->second;
			++sellIter;
		}
		_snprintf_s(buff,1023,"-------Buy\t%6d\t%5d\tSell\t%6d\t%5d",buyPrice,buyQuantity,sellPrice,sellQuantity);
		LightLogger::Info(buff);
	}
}

void ContractMatcher::LogMatchItems()
{
	char buff[1024] = "";
	for (MatchItems::const_iterator iter = m_allItems.begin();
		iter != m_allItems.end();
		++iter)
	{
		_snprintf_s(buff,1023,"-------%6d\t%6d\t%6d\t%d\t%d\t%d\t%d",
			iter->buyPrice,iter->currentPrice,iter->sellPrice,
			iter->buyQuantity,iter->sellQuantity,iter->matched,iter->buyQuantity-iter->sellQuantity
			);
		LightLogger::Info(buff);
	}
}


int ContractMatcher::Match()
{
	PendingMarketMap::const_reverse_iterator buyIter = GetBuys().rbegin();
	PendingMarketMap::const_iterator sellIter = GetSells().begin();
	m_last_bi = buyIter;
	m_last_si = sellIter;
	GenerateMatchItems(buyIter, sellIter, m_last_bi, m_last_si);

	int result = 0;
	if (!m_matchItems.empty()) 
	{
		MatchItem& lastItem = m_matchItems.back();
		if (lastItem.buyQuantity == lastItem.sellQuantity)
		{
			AdjustMatchPrice(lastItem, buyIter, sellIter, m_last_bi, m_last_si);
		}
		result = lastItem.currentPrice;
	}
	GenerateAllItems(m_last_bi,m_last_si);
//	GenerateItems();

	return result;
}

bool ContractMatcher::NextBuyGreaterSell(PendingMarketMap::const_reverse_iterator last_bi,PendingMarketMap::const_iterator last_si) const
{
	++last_bi;
	int sp = last_si->first;
	int bp = last_bi->first;
	return bp >= sp;
}

bool ContractMatcher::BuyGreaterNextSell(PendingMarketMap::const_reverse_iterator last_bi,PendingMarketMap::const_iterator last_si) const
{
	++last_si;
	int sp = last_si->first;
	int bp = last_bi->first;
	return bp >= sp;
}

void ContractMatcher::AdjustMatchPrice(MatchItem& lastItem, 
									   PendingMarketMap::const_reverse_iterator buyIter, 
									   PendingMarketMap::const_iterator sellIter, 
									   PendingMarketMap::const_reverse_iterator last_bi, 
									   PendingMarketMap::const_iterator last_si)
{
	int cp = lastItem.currentPrice;
	if (buyIter == GetBuys().rend() && sellIter == GetSells().end())
	{
		int avg = (last_bi->first + last_si->first) / 2;
		cp = avg - avg % m_minPriceUnit;
	}
	if (buyIter != GetBuys().rend() && NextBuyGreaterSell(last_bi,last_si))
	{
		if (sellIter != GetSells().end() && BuyGreaterNextSell(last_bi,last_si))
		{
			++last_bi;
			++last_si;
			cp = last_bi->second > last_si->second ?
				last_bi->first : 
				last_si->first;
		}
		else
		{
			cp = last_bi->first;
		}
	}
	else if (sellIter != GetSells().end() && BuyGreaterNextSell(last_bi,last_si))
	{
		cp = last_si->first;
	}
	lastItem.currentPrice = cp;
}

void ContractMatcher::GenerateMatchItems(PendingMarketMap::const_reverse_iterator &buyIter, PendingMarketMap::const_iterator &sellIter, PendingMarketMap::const_reverse_iterator& last_bi, PendingMarketMap::const_iterator& last_si)
{
	m_matchItems.clear();
	MatchItem item(0,0,0,0,0,0);
	while (buyIter != GetBuys().rend() && sellIter != GetSells().end())
	{
		if (item.buyQuantity == 0)
		{
			item.buyPrice = buyIter->first;
			item.buyQuantity = buyIter->second;
		}
		if (item.sellQuantity == 0)
		{
			item.sellPrice = sellIter->first;
			item.sellQuantity = sellIter->second;
		}

		if (item.buyPrice < item.sellPrice) break;
		last_bi = buyIter;
		last_si = sellIter;
		if(item.buyQuantity >= item.sellQuantity)
		{
			item.matched += item.sellQuantity;
			item.currentPrice = item.buyPrice;
			m_matchItems.push_back(item);
			item.buyQuantity -= item.sellQuantity;
			item.sellQuantity = 0;
			++sellIter;
			if (item.buyQuantity == 0) ++buyIter;
		}
		else
		{
			item.matched += item.buyQuantity;
			item.currentPrice = item.sellPrice;
			m_matchItems.push_back(item);
			item.sellQuantity -= item.buyQuantity;
			item.buyQuantity = 0;
			item.matched += item.buyQuantity;
			++buyIter;
		}
	}
}


void ContractMatcher::GenerateAllItems(PendingMarketMap::const_reverse_iterator last_bi, PendingMarketMap::const_iterator last_si)
{
	PendingMarketMap::const_reverse_iterator bend = GetBuys().rend();
	PendingMarketMap::const_iterator send = GetSells().end();

	m_allItems = m_matchItems;
	MatchItem lastItem = m_matchItems.empty() ? MatchItem(0,0,0,0,0,0) : m_matchItems.back();
	int remain = lastItem.buyQuantity - lastItem.sellQuantity;
	int bp,bq,sp,sq = 0;
	if (remain > 0)
	{
		bp = last_bi->first;
		bq = remain;
		NextSell(last_si,send,sp,sq);
		m_allItems.push_back(MatchItem(bp,lastItem.currentPrice,sp,bq,sq,lastItem.matched));

	}
	else if (remain < 0)
	{
		sp = last_si->first;
		sq = -remain;
		NextBuy(last_bi,bend,bp,bq);
		m_allItems.push_back(MatchItem(bp,lastItem.currentPrice,sp,bq,sq,lastItem.matched));
	}

	while (last_bi != bend || last_si != send)
	{
		NextBuy(last_bi,bend,bp,bq);
		NextSell(last_si,send,sp,sq);
		if (bp > 0 || sp > 0)
		{
			m_allItems.push_back(MatchItem(bp,lastItem.currentPrice,sp,bq,sq,lastItem.matched));
		}
	}
}

void ContractMatcher::NextBuy(PendingMarketMap::const_reverse_iterator& bi, PendingMarketMap::const_reverse_iterator& end, int& bp, int& bq)
{
	if (bi != end) ++bi;
	if (bi == end)
	{
		bp = 0;
		bq = 0;
	}
	else
	{
		bp = bi->first;
		bq = bi->second;
	}
}

void ContractMatcher::NextSell(PendingMarketMap::const_iterator& si,PendingMarketMap::const_iterator& end, int& sp, int& sq)
{
	if (si != end) ++si;
	if (si == end)
	{
		sp = 0;
		sq = 0;
	}
	else
	{
		sp = si->first;
		sq = si->second;
	}
}

void ContractMatcher::GenerateBuys(PendingMarketMap::const_reverse_iterator last_bi)
{
	m_buyOrderItems.clear();
	int match_i = m_matchItems.size() - 1;
	if (match_i < 0) return;
	PriceMap bps;
	GenerateBuyPrices(last_bi,bps);
	for(PriceMap::const_iterator iter = bps.begin();
		iter != bps.end();
		++iter)
	{
		GenerateBuyQuantity(iter->first,iter->second);
	}
}   

void ContractMatcher::GenerateBuyPrices(PendingMarketMap::const_reverse_iterator bi,PriceMap& out_bps)
{
	size_t si = m_matchItems.size();
	while (si < m_allItems.size())
	{
		MatchItem const& item = m_allItems[si];
		if (item.sellPrice== 0) break;
		out_bps[item.sellPrice] = false;
		++si;
	}

	PendingMarketMap::const_reverse_iterator begin = GetBuys().rbegin();

	out_bps[begin->first + m_minPriceUnit] = true;
	while (true)
	{
		int bp = bi->first;
		out_bps[bp] = false;
		out_bps[bp+m_minPriceUnit] = true;
		if (bi == begin)
		{
			break;
		}
		--bi;
	}
}

void PushOrderItem(OrderItems& items,OrderItem const& item)
{
	if (items.empty())
	{
		items.push_back(item);
	}
	else if (items.back().quantity != item.quantity)
	{
		items.push_back(item);
	}
}

void ContractMatcher::GenerateBuyQuantity(int buyPrice,bool moreFlag)
{
	size_t match_i = m_matchItems.size() - 1;
	int bi = match_i;
	for(;bi >= 0;--bi)
	{
		if (m_allItems[bi].buyPrice >= buyPrice) 
			break;
	}
	int totalQuantity = 0;
	for (size_t si = bi + 1;si < m_allItems.size(); ++si)
	{
		MatchItem const& item = m_allItems[si];
		if (item.sellPrice > buyPrice) break;
		int q = (si <= match_i) ? 
			min(item.buyQuantity,item.sellQuantity) :
			item.sellQuantity;
		totalQuantity += q;
	}
	if (moreFlag && totalQuantity > 0)
	{
		PendingMarketMap::const_iterator iter = GetSells().find(buyPrice);
		int sellQuantity = iter != GetSells().end() ? iter->second : 0;
		int q = totalQuantity-sellQuantity-1;

		if (q > 0) 
		{
			int dealPrice = m_allItems[bi+1].buyPrice;
			PushOrderItem(m_buyOrderItems,OrderItem(buyPrice,dealPrice,q));
		}
	}
	if (totalQuantity > 0)
	{
		PushOrderItem(m_buyOrderItems,OrderItem(buyPrice,buyPrice,totalQuantity));
	}
}


void ContractMatcher::GenerateSells(PendingMarketMap::const_iterator last_si)
{
	m_sellOrderItems.clear();
	int match_i = m_matchItems.size() - 1;
	if (match_i < 0) return;
	PriceMap sps;
	GenerateSellPrices(last_si,sps);
	for(PriceMap::const_reverse_iterator iter = sps.rbegin();
		iter != sps.rend();
		++iter)
	{
		GenerateSellQuantity(iter->first,iter->second);
	}
}   

void ContractMatcher::GenerateSellPrices(PendingMarketMap::const_iterator si,PriceMap& out_sps)
{
	size_t bi = m_matchItems.size();
	while (bi < m_allItems.size())
	{
		MatchItem const& item = m_allItems[bi];
		if (item.buyPrice== 0) break;
		out_sps[item.buyPrice] = false;
		++bi;
	}

	PendingMarketMap::const_iterator begin = GetSells().begin();

	out_sps[begin->first - m_minPriceUnit] = true;
	while (true)
	{
		int sp = si->first;
		out_sps[sp] = false;
		out_sps[sp-m_minPriceUnit] = true;
		if (si == begin)
		{
			break;
		}
		--si;
	}
}   

void ContractMatcher::GenerateSellQuantity(int sellPrice,bool moreFlag)
{
	size_t match_i = m_matchItems.size() - 1;
	int si = match_i;
	for(;si >= 0;--si)
	{
		if (m_allItems[si].sellPrice <= sellPrice) 
			break;
	}
	int totalQuantity = 0;
	int last_bp = 0;
	for (size_t bi = si + 1;bi < m_allItems.size(); ++bi)
	{
		MatchItem const& item = m_allItems[bi];
		if (item.buyPrice < sellPrice) break;
		int q = (bi <= match_i) ? 
			min(item.buyQuantity,item.sellQuantity) :
			item.buyQuantity;
		totalQuantity += q;
		last_bp = item.buyPrice;
	}
	if (moreFlag && totalQuantity > 0)
	{
		PendingMarketMap::const_iterator iter = GetBuys().find(sellPrice);
		int buyQuantity = iter != GetBuys().end() ? iter->second : 0;
		int q = totalQuantity-buyQuantity-1;
		if (q > 0) 
		{
			int dealPrice = m_allItems[si+1].sellPrice;
			PushOrderItem(m_sellOrderItems,OrderItem(sellPrice,dealPrice,q));
		}
	}
	int price = last_bp;
	size_t nextSi = si + 1;
	if (nextSi < m_allItems.size())
	{
		MatchItem const& item = m_allItems[nextSi];
		if (item.sellPrice <= price && item.sellQuantity > 0)
		{
			price = sellPrice;
		}
	}
	if (totalQuantity > 0)
	{
		PushOrderItem(m_sellOrderItems,OrderItem(sellPrice,price,totalQuantity));
	}
}

void ContractMatcher::GenerateItems()
{
//	GenerateAllItems(m_last_bi, m_last_si);
	GenerateBuys(m_last_bi);
	GenerateSells(m_last_si);
}

void ContractMatcher::Clear()
{
	m_markets.Clear();
}


ContractMatcher& ContractMatcheres::GetMatcher(int contract)
{
	ContractMatcher* matcher = m_matcheres[contract];
	if (IsNull(matcher))
	{
		LightLogger::Error(FT("ContractMatchers GetMatcher(%d) throw 1",contract));
		throw 1;
	}
	return *matcher;
}

void ContractMatcheres::AddSell(int contract,int price,int quantity)
{
	GetMatcher(contract).AddBuy(price,quantity);
}

void ContractMatcheres::AddBuy(int contract,int price,int quantity)
{
	GetMatcher(contract).AddSell(price,quantity);
}

void ContractMatcheres::Update(PendingMarket const& pendingMarket)
{
	if (HasContract(pendingMarket.contract))
	{
		GetMatcher(pendingMarket.contract).Update(pendingMarket);
	}
}

ContractMatcher& ContractMatcheres::AddMatcher(int contract, int minPirceUnit)
{
	ContractMatcher* matcher = m_matcheres[contract];
	if (IsNull(matcher))
	{
		m_matcheres[contract] = new ContractMatcher(contract,minPirceUnit);
	}
	return *m_matcheres[contract];
}

ContractMatcheres::ContractMatcheres()
{
	memset(m_matcheres,0,sizeof(m_matcheres));
}


ContractMatcheres::~ContractMatcheres()
{
	for (int i = 0; i < MAX_MARKET_COUNT; ++i)
	{
		ContractMatcher* matcher = m_matcheres[i];
		if (NotNull(matcher))
		{
			delete matcher;
		}
	}
}

void ContractMatcheres::Clear()
{
	for (int i = 0; i < MAX_MARKET_COUNT; ++i)
	{
		ContractMatcher* matcher = m_matcheres[i];
		if (NotNull(matcher))
		{
			matcher->Clear();
		}
	}
}